package org.zoomdev.zoom.web.action;

import jakarta.servlet.ServletException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zoomdev.zoom.aop.MethodInvokerFactory;
import org.zoomdev.zoom.common.exceptions.ZoomException;
import org.zoomdev.zoom.common.filter.Filter;
import org.zoomdev.zoom.common.utils.Classes;
import org.zoomdev.zoom.common.utils.CollectionUtils;
import org.zoomdev.zoom.ioc.Ioc;
import org.zoomdev.zoom.ioc.impl.JavaMethodParameterNamesGetter;
import org.zoomdev.zoom.web.annotations.Param;
import org.zoomdev.zoom.web.modules.CoreWebModule;
import org.zoomdev.zoom.web.parameter.ParameterParserFactory;
import org.zoomdev.zoom.web.parameter.PreParameter;
import org.zoomdev.zoom.web.parameter.PreParameterParserManager;
import org.zoomdev.zoom.web.rendering.RenderingFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.regex.Pattern;

/**
 * 描述一个 Action的配置，
 * 1、哪个类的哪个方法
 * 2、路径
 */
public final class MethodAction {
    static final Filter<Annotation> filter = value -> value instanceof Param;
    private static final Logger log = LoggerFactory.getLogger(CoreWebModule.class);
    private static final String DEFAULT_ENCODING = "utf8";
    private static final Pattern CORRECT_PATTERN = Pattern.compile("^[a-zA-Z][a-zA-Z0-9_]*$");
    private final static JavaMethodParameterNamesGetter getter = new JavaMethodParameterNamesGetter();
    private final RenderingFactory renderingFactory;
    private final ParameterParserFactory parameterParserFactory;
    private final PreParameterParserManager preParameterParserManager;
    private final ActionInterceptorFactory actionInterceptorFactory;
    private final MethodInvokerFactory methodInvokerFactory;
    private final Ioc ioc;

    public MethodAction(Ioc ioc,
                        RenderingFactory renderingFactory,
                        ParameterParserFactory parameterParserFactory,
                        PreParameterParserManager preParameterParserManager,
                        ActionInterceptorFactory actionInterceptorFactory
    ) {
        this.ioc = ioc;
        this.renderingFactory = renderingFactory;
        this.parameterParserFactory = parameterParserFactory;
        this.preParameterParserManager = preParameterParserManager;
        this.actionInterceptorFactory = actionInterceptorFactory;
        this.methodInvokerFactory = MethodInvokerFactory.FACTORY;
    }

    public void handle(ActionContext context) throws ServletException {
        MethodActionHandler config = context.getAction();

        try {
            if (config.instance == null) {
                synchronized (config) {
                    if (config.instance == null) {
                        initAction(config);
                    }
                }
            }
            handleProcess(context, config);
        } catch (Throwable e) {
            // 错误处理
            handleError(context, config, Classes.getCause(e));
        } finally {
            // 这里可以释放资源等操作
            handleRelease(context, config);
        }
    }

    private void initAction(MethodActionHandler config) {
        try {
            if (config.rendering == null) {
                config.rendering = renderingFactory.createRendering(config.controller, config.method);
            }
        } catch (Throwable e) {
            // 出现异常时重置所有相关属性为null
            log.error("创建Action失败", e);
            throw new ZoomException("创建Action失败", e);
        }


        try {
            if (config.preInterceptors == null) {
                config.preInterceptors = actionInterceptorFactory.create(ActionInterceptorFactory.PRE_PARSE, config.controller, config.method);
                config.parseInterceptors = actionInterceptorFactory.create(ActionInterceptorFactory.PARSE, config.controller, config.method);
                config.resultInterceptors = actionInterceptorFactory.create(ActionInterceptorFactory.RESULT, config.controller, config.method);
                config.errorInterceptors = actionInterceptorFactory.create(ActionInterceptorFactory.ERROR, config.controller, config.method);
                config.completeInterceptors = actionInterceptorFactory.create(ActionInterceptorFactory.COMPLETE, config.controller, config.method);
                config.beforeInvokeInterceptors = actionInterceptorFactory.create(ActionInterceptorFactory.BEFORE_INVOKE, config.controller, config.method);
            }

        } catch (Throwable e) {
            config.preInterceptors = null;
            config.parseInterceptors = null;
            config.resultInterceptors = null;
            config.errorInterceptors = null;
            config.beforeInvokeInterceptors = null;
            config.completeInterceptors = null;
            log.error("创建Action失败", e);
            throw new ZoomException("创建Action失败", e);
        }

        try {
            if (config.instance == null) {
                config.instance = ioc.fetch(config.controller);
                config.parameterNames = getNames(config.controller, config.method);
                config.parameterParser = parameterParserFactory.createParameterParser(config.method, config.parameterNames);
                config.preParamParser = preParameterParserManager;
                config.invoker = methodInvokerFactory.create(config.method);
            }
        } catch (Throwable e) {
            config.instance = null;
            log.error("创建Action失败,Instance没有创建成功", e);
            throw new ZoomException("创建Action失败,Instance没有创建成功", e);
        }


    }

    private void handleProcess(ActionContext context, MethodActionHandler config) throws Throwable {
        switch (context.getState()) {

            case ActionContext.STATE_INIT: {
                String encoding = config.encoding == null ? DEFAULT_ENCODING : config.encoding;

                context.getRequest().setCharacterEncoding(encoding);
                context.getResponse().setCharacterEncoding(encoding);
                context.setState(ActionContext.STATE_PRE_PARSE);
            }

            case ActionContext.STATE_PRE_PARSE: {
                if (interceptPreParse(context, config)) {
                    return;
                }
                if (context.getState() == ActionContext.STATE_PRE_PARSE) {
                    PreParameter data = config.preParamParser.preParse(context);
                    context.setPreParameter(data);
                }
            }
            case ActionContext.STATE_PARSE: {
                this.interceptParse(context, config);
                if (context.getState() == ActionContext.STATE_PARSE) {
                    Object[] args = config.parameterParser.parse(context);
                    context.setArgs(args);
                }
            }

            case ActionContext.STATE_BEFORE_INVOKE: {
                //beforeInvoke
                this.interceptBeforeInvoke(context, config);
                Object result = config.invoker.invoke(config.instance, context.getArgs());
                context.setResult(result);
                this.interceptResult(context, config);
            }

            case ActionContext.STATE_BEFORE_RENDER: {
                config.rendering.render(context);
            }
        }
    }

    private void handleError(ActionContext context, MethodActionHandler config, Throwable ex) throws ServletException {
        context.setException(ex);
        context.setState(ActionContext.STATE_BEFORE_RENDER);
        try {
            this.interceptorHandleError(context, config);
            config.rendering.render(context);
        } catch (Exception e) {
            log.error("在处理错误阶段产生无法处理的异常,本异常将由web容器直接处理", e);
            throw new ServletException(e);
        }
    }

    private void handleRelease(ActionContext context, MethodActionHandler config) {
        context.setState(ActionContext.STATE_AFTER_RENDER);
        interceptComplete(context, config);
        try {
            release(context);
        } catch (Exception e) {
            //这里如果有异常,那么只是记录一下
            log.error("在release阶段发生异常", e);
        }
    }

    private static String[] getNames(Class controllerClass, Method method) {
        String[] names = getter.getParameterNames(controllerClass, method);
        Annotation[][] paramAnnotations = method.getParameterAnnotations();
        int c = names.length;
        for (int i = 0; i < c; ++i) {
            Annotation[] annotations = paramAnnotations[i];
            Param param = (Param) CollectionUtils.get(annotations, filter);
            if (param != null) {
                String name = param.name();
                if (!StringUtils.isEmpty(name)) {
                    if (name.startsWith("{") && name.endsWith("}")) {
                        name = name.substring(1, name.length() - 1);
                    }
                    if (!CORRECT_PATTERN.matcher(name).matches()) {
                        throw new ZoomException("Param标注的名称格式异常，非字母开头，含有字母数字的格式" + name);
                    }
                    names[i] = name;
                }
            }
        }
        return names;
    }

    private boolean interceptPreParse(ActionContext context, MethodActionHandler config) throws Exception {
        if (config.preInterceptors != null) {
            for (PreParseInterceptor actionInterceptor : config.preInterceptors) {
                if (context.getState() == ActionContext.STATE_PRE_PARSE) {
                    if (!actionInterceptor.preParse(context)) {
                        //结束switch
                        context.setState(ActionContext.STATE_AFTER_RENDER);
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private void interceptParse(ActionContext context, MethodActionHandler config) throws Exception {
        if (config.parseInterceptors != null) {
            for (ParseInterceptor actionInterceptor : config.parseInterceptors) {
                if (context.getState() == ActionContext.STATE_PARSE) {
                    actionInterceptor.parse(context);
                }
            }
        }
    }

    private void interceptBeforeInvoke(ActionContext context, MethodActionHandler config) throws Exception {
        if (config.beforeInvokeInterceptors != null) {
            for (BeforeInvokeInterceptor actionInterceptor : config.beforeInvokeInterceptors) {
                if (context.getState() == ActionContext.STATE_BEFORE_INVOKE) {
                    actionInterceptor.beforeInvoke(context);
                }
            }
        }
    }


    private void interceptResult(ActionContext context, MethodActionHandler config) throws Exception {
        if (config.resultInterceptors != null) {
            for (ResultInterceptor actionInterceptor : config.resultInterceptors) {
                if (context.getState() == ActionContext.STATE_BEFORE_RENDER) {
                    actionInterceptor.whenResult(context);
                }
            }
        }
    }

    private void interceptorHandleError(ActionContext context, MethodActionHandler config) {
        if (config.errorInterceptors != null) {
            for (ErrorInterceptor actionInterceptor : config.errorInterceptors) {
                try {
                    if (!actionInterceptor.whenError(context)) {
                        return;
                    }
                } catch (Exception e) {
                    throw new ZoomException("调用拦截器的whenError发生异常", e);
                }
            }
        }
    }

    private void interceptComplete(ActionContext context, MethodActionHandler config) {
        if (config.completeInterceptors != null) {
            for (CompleteInterceptor actionInterceptor : config.completeInterceptors) {
                try {
                    actionInterceptor.complete(context);
                } catch (Exception e) {
                    log.error("在调用拦截器的complete的时候发生异常", e);
                }
            }
        }
    }

    private void release(ActionContext context) {
        context.destroy();
    }
}
